using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Xml.Serialization;
using System.ServiceModel;
using System.Threading;
using Spike.ContractBuilder;
using System.Reflection;

namespace Spike.TestHarness
{
    public partial class MainForm : Form
    {
        const string TESTCASES_DIR = "TestCases";

        public MainForm()
        {
            InitializeComponent();

            StringBuilder sb = new StringBuilder();

            //attempt to load files
            sb.Append(RefreshClientContracts());

            if (Directory.Exists(TESTCASES_DIR))
            {
                foreach (string filename in Directory.GetFiles(TESTCASES_DIR))
                {
                    try
                    {
                        TestCase test = SerializationHelper<TestCase>.DeserializeFromFile(filename);
                        test.VerifyAssemblyReference();
                        AddTestCaseControl(test);
                    }
                    catch (Exception ex)
                    {
                        ContractGenerator.Instance.MoveToFailedToLoad(filename);

                        sb.Append("There was an error refreshing the Test Cases.");
                        sb.Append(Environment.NewLine);
                        sb.Append("\tException: " + ex.GetType().Name);
                        sb.Append("\tMessage: " + ex.Message);
                        sb.Append(Environment.NewLine);
                    }
                }
            }
            else
                Directory.CreateDirectory(TESTCASES_DIR);

            if (sb.Length > 0)
            {
                // show error message dialog
                ErrorDialog dlg = new ErrorDialog();
                dlg.ErrorMessage = sb.ToString();
                dlg.ShowDialog();
            }
        }

        private void ExecuteButton_Click(object sender, EventArgs e)
        {
            EnableButtons(false);
            if (ValidTestCases())
            {
                TestCaseControl cntrl = tabControl1.SelectedTab.Controls[0] as TestCaseControl;

                PerformTests(cntrl, int.Parse(txtRepeat.Text));
            }
            EnableButtons(true);
        }

        private void PerformTests(TestCaseControl testControl, int repeat)
        {
            PerformTests(new TestCaseControl[] { testControl }, repeat);
        }

        List<Thread> _threads = new List<Thread>();

        private void PerformTests(TestCaseControl[] tests, int repeatNumber)
        {
            DateTime start = DateTime.Now;

            for (int i = 0; i < repeatNumber; i++)
            {

                foreach (TestCaseControl cntrl in tests)
                {
                    // include a delay
                    int delay = int.Parse(txtDelay.Text) * 1000;
                    Thread.CurrentThread.Join(delay);

                    TestCase testcase = cntrl.Case;

                    testcase.LastExecuted = start;

                    // run the test                    
                    TestCaseResult result = new TestCaseResult(string.Empty);
                    cntrl.AddTestCaseResult(result);

                    TestThread test = new TestThread(testcase, result, TestResultCallback);

                    if (_threads.Count >= int.Parse(txtNumThreads.Text))
                    {
                        bool waitingToStart = true;
                        while (waitingToStart)
                        {
                            // wait until a thread finishes
                            Thread.CurrentThread.Join(500);

                            foreach (Thread thread in _threads)
                            {
                                if (thread.ThreadState == ThreadState.Stopped)
                                {
                                    Thread newThread = new Thread(test.RunTest);
                                    newThread.Start();
                                    waitingToStart = false;

                                    _threads.Remove(thread);
                                    break;
                                }
                            }
                        }

                    }
                    else
                    {
                        Thread thread = new Thread(test.RunTest);
                        thread.Start();
                        _threads.Add(thread);
                    }
                }
            }
        }

        public void TestResultCallback(TestCaseResult.STATUS status)
        {
            string s = status.ToString();
        }

        private bool ValidTestCases()
        {
            bool isValid = true;
            // make sure there is more than one test
            if (tabControl1.TabCount == 0)
            {
                MessageBox.Show("Please create a new or load an existing test.");
                isValid = false;
            }

            return isValid;
        }

        private void bntExecuteAll_Click(object sender, EventArgs e)
        {
            EnableButtons(false);
            if (ValidTestCases())
            {
                TestCaseControl[] cntrls = new TestCaseControl[tabControl1.TabPages.Count];

                for (int i = 0; i < tabControl1.TabPages.Count; i++)
                {
                    TestCaseControl cntrl = tabControl1.TabPages[i].Controls[0] as TestCaseControl;
                    cntrls[i] = cntrl;
                }
                PerformTests(cntrls, int.Parse(txtRepeat.Text));
            }
            EnableButtons(true);
        }

        private void newTestCaseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                using (NewTestCaseDialog dlg = new NewTestCaseDialog())
                {
                    ServiceClientContract[] contracts = new ServiceClientContract[ContractBuilder.ContractGenerator.Instance.Contracts.Count];
                    ContractBuilder.ContractGenerator.Instance.Contracts.Values.CopyTo(contracts, 0);

                    List<string> existingTestCases = new List<string>();
                    foreach (TabPage page in tabControl1.TabPages)
                    {
                        existingTestCases.Add(page.Text);
                    }

                    dlg.SetCollection(contracts, existingTestCases);

                    if (dlg.ShowDialog() == DialogResult.OK)
                    {
                        TestCase test = new TestCase(dlg.TestName, dlg.Contract, dlg.Endpoint, dlg.Assembly, dlg.Method, dlg.URI);
                        AddTestCaseControl(test);
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Failed to add Test Case: " + ex.Message);
            }
        }

        private void AddTestCaseControl(TestCase test)
        {
            TestCaseControl cntrl = new TestCaseControl();
            cntrl.Case = test;
            cntrl.Dock = DockStyle.Fill;
            cntrl.Validated += new EventHandler(cntrl_Validated);

            TabPage page = new TabPage(test.Name);
            page.Controls.Add(cntrl);

            tabControl1.TabPages.Add(page);

            tabControl1.SelectedTab = page;
        }

        void cntrl_Validated(object sender, EventArgs e)
        {
            // TODO: get validation to disable and enable buttons
            // this works but does not enable buttons if tabbing from one tab to another
            //if (sender is TestCaseControl)
            //{
            //    EnableButtons(((TestCaseControl)sender).IsValidTestCase);
            //}
        }

        private void loadTestCaseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.openFileDialog1.FileName = "";
            this.openFileDialog1.Filter = "XML Files|*.xml";
            this.openFileDialog1.Title = "Retrieve Input File";

            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                foreach (string filename in openFileDialog1.FileNames)
                {
                    TestCase test = TestCase.Load(filename);
                    test.VerifyAssemblyReference();
                    AddTestCaseControl(test);
                }
            }
        }

        private void saveTestCaseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            TestCaseControl cntrl = tabControl1.SelectedTab.Controls[0] as TestCaseControl;
            saveFileDialog1.InitialDirectory = Path.Combine(Environment.CurrentDirectory, TESTCASES_DIR);
            saveFileDialog1.FileName = cntrl.Case.Name;

            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                cntrl.Case.Save(saveFileDialog1.FileName);
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void EnableButtons(bool shouldEnable)
        {
            btnExecuteAll.Enabled = shouldEnable;
            btnExecuteSingle.Enabled = shouldEnable;
            txtDelay.Enabled = shouldEnable;
            txtNumThreads.Enabled = shouldEnable;
            txtRepeat.Enabled = shouldEnable;
            Cursor.Current = shouldEnable ? Cursors.Default : Cursors.WaitCursor;
        }

        private void addViaURLToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AddUrlDialog url = new AddUrlDialog();
            if(DialogResult.OK==  url.ShowDialog())
                RefreshClientContracts();
        }

        private void addToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.openFileDialog1.FileName = "";
            this.openFileDialog1.Filter = "Libraries or Executables | *.exe;*.dll";
            this.openFileDialog1.Title = "Select file containing the service contract.";

            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                ContractGenerator.Instance.AddAssembly(openFileDialog1.FileName);
                ContractGenerator.Instance.RefreshProxies();
            }
        }


        private Host.ManageHosts _hostDlg = new Spike.TestHarness.Host.ManageHosts();

        private void showHostingWindowToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_hostDlg.Visible)
            {
                _hostDlg.Hide();
            }
            else
            {
                _hostDlg.RefreshHosts();

                _hostDlg.Show();
            }

        }

        #region Client and Assembly Menus
        private string RefreshClientContracts()
        {
            StringBuilder sb = new StringBuilder();
            try
            {
                ContractGenerator.Instance.RefreshProxies();

                // clear only the added items
                for (int i = clientToolStripMenuItem.DropDownItems.Count - 1; i > 1; i--)
                {
                    ToolStripItem mnu = clientToolStripMenuItem.DropDownItems[i];

                    //only the added items will have a contract.Filename value as the tag
                    if (mnu.Tag is string)
                    {
                        clientToolStripMenuItem.DropDownItems.Remove(mnu);
                    }
                }

                for (int i = assemblyToolStripMenuItem.DropDownItems.Count - 1; i > 1; i--)
                {
                    ToolStripItem mnu = assemblyToolStripMenuItem.DropDownItems[i];
                    //only the added items will have a assembly.name value as the tag
                    if (mnu.Tag is string)
                    {
                        assemblyToolStripMenuItem.DropDownItems.Remove(mnu);
                    }
                }

                foreach (ServiceClientContract contract in ContractGenerator.Instance.Contracts.Values)
                {
                    ToolStripMenuItem mnu = new ToolStripMenuItem();
                    mnu.Name = contract.Name;
                    mnu.Text = contract.Name;
                    mnu.Tag = contract.Name;

                    ToolStripMenuItem mnuEdit = new ToolStripMenuItem("Edit Configuration", null, new EventHandler(mnu_Click), "EditClientConfiguration");
                    mnuEdit.Tag = contract.FileName;
                    ToolStripMenuItem mnuRemove = new ToolStripMenuItem("Remove", null, new EventHandler(mnu_Click), "RemoveClientConfiguration");
                    mnuRemove.Tag = contract.FileName;
                    mnu.DropDownItems.Add(mnuEdit);
                    mnu.DropDownItems.Add(mnuRemove);
                    clientToolStripMenuItem.DropDownItems.Add(mnu);
                }

                foreach (Assembly assembly in ContractGenerator.Instance.Assemblies.Values)
                {
                    ToolStripMenuItem mnu = new ToolStripMenuItem();
                    mnu.Name = assembly.FullName;
                    mnu.Text = Path.GetFileName(assembly.Location);
                    mnu.Tag = assembly.FullName;
                    ToolStripMenuItem mnuRemove = new ToolStripMenuItem("Remove", null, new EventHandler(mnu_Click), "RemoveAssembly");
                    mnuRemove.Tag = assembly.Location;
                    mnu.DropDownItems.Add(mnuRemove);
                    assemblyToolStripMenuItem.DropDownItems.Add(mnu);
                }
            }
            catch (Exception ex)
            {
                sb.Append("There was an error refreshing the Client Contracts.");
                sb.Append(Environment.NewLine);
                sb.Append("\tException: " + ex.GetType().Name);
                sb.Append("\tMessage: " + ex.Message);
                sb.Append(Environment.NewLine);
            }

            return sb.ToString();
        }

        void mnu_Click(object sender, EventArgs e)
        {
            ToolStripMenuItem mnu = (ToolStripMenuItem)sender;

            switch (mnu.Name)
            {
                case "EditClientConfiguration":
                    System.Diagnostics.Process.Start("Utilities/SvcConfigEditor.exe", "\"" + mnu.Tag as string + "\"");
                    break;
                case "RemoveClientConfiguration":
                    ContractBuilder.ContractGenerator.Instance.RemoveClientConfiguration(mnu.Tag as string);
                    RefreshClientContracts();
                    break;
                case "RemoveAssembly":
                    ContractBuilder.ContractGenerator.Instance.RemoveAssembly(mnu.Tag as string);
                    RefreshClientContracts();
                    break;
            }
        }
        #endregion
    }
}